package _map

import (
	"encoding/json"
	"fmt"
	"hash/fnv"
	"reflect"
)

type node[K, V any] struct {
	key   K
	value V
	Next  *node[K, V]
}

type HashMap[K, V any] struct {
	Table      []*node[K, V]
	size, Len  uint32
	loadFactor float32
}

func NewHashMap[K, V any](size uint32) *HashMap[K, V] {
	re := HashMap[K, V]{
		Table:      make([]*node[K, V], size),
		size:       size,
		loadFactor: 0.75,
	}
	return &re
}

func (hm *HashMap[K, V]) check() {
	lf := uint32(float32(hm.size) * hm.loadFactor)
	if hm.Len >= lf {
		hm.resize()
	}
}

func (hm *HashMap[K, V]) resize() {
	oldSize := hm.size
	hm.size = hm.size * 2
	newTable := make([]*node[K, V], hm.size)

	for i := uint32(0); i < oldSize; i++ {
		current := hm.Table[i]
		if current == nil {
			continue
		}
		for current != nil {
			position := hm.hash(current.key)
			if newTable[position] == nil {
				newTable[position] = current
				current = current.Next
				newTable[position].Next = nil
			} else {
				list := newTable[position]
				for {
					if list.Next == nil {
						list.Next = current
						current = current.Next
						list.Next.Next = nil
						break
					}
					list = list.Next
				}
			}
		}
		hm.Table = newTable
	}
}

func (hm *HashMap[K, V]) hash(key K) uint32 {
	hc := fnv.New32()
	json, _ := json.Marshal(key)
	hc.Write(json)
	return uint32(hc.Sum32() % hm.size)
}

func (hm *HashMap[K, V]) Put(key K, val V) {
	hm.check()
	position := hm.hash(key)
	current := hm.Table[position]
	if current == nil {
		hm.Table[position] = &node[K, V]{
			key:   key,
			value: val,
		}
		hm.Len++
		return
	}

	for {
		if reflect.DeepEqual(current.key, key) {
			current.value = val
			return
		}
		if current.Next == nil {
			break
		}
		current = current.Next
	}
	current.Next = &node[K, V]{
		key:   key,
		value: val,
	}
	hm.Len++
}

func (hm *HashMap[K, V]) Get(key K) (V, error) {
	position := hm.hash(key)
	current := hm.Table[position]
	for current != nil {
		if reflect.DeepEqual(current.key, key) {
			return current.value, nil
		}
		current = current.Next
	}
	return node[K, V]{}.value, fmt.Errorf("no Value")
}

func (hm *HashMap[K, V]) Remove(key K) error {
	position := hm.hash(key)
	current := hm.Table[position]
	if current == nil {
		return fmt.Errorf("no Value")
	}
	defer func() { hm.Len-- }()
	if reflect.DeepEqual(key, hm.Table[position].key) {
		hm.Table[position] = hm.Table[position].Next
		return nil
	}

	for {
		if reflect.DeepEqual(key, current.key) {
			current = current.Next
			return nil
		}
		current = current.Next
	}

	return fmt.Errorf("no Value")
}
